﻿using System;
using Xenta.Attributes;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Operations;
using Xenta.Utils;

namespace Xenta.Plugins
{
    /// <summary>
    /// Represents the fixed discount rule handler.
    /// </summary>
    [Dependency("dcb99a98-4c36-4f99-8084-610c25fbbec0")]
    public sealed class FixedDiscountRuleHandler : ISalesRuleHandler
    {
        public int Apply(IUnitOfWork uow, 
            SalesRuleEntity rule, 
            OrderEntity order,
            out decimal discountAmount,
            out decimal handlingFee,
            out decimal shippingPrice,
            out decimal taxAmount,
            out decimal total)
        {
            var discount = rule as FixedDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");

            discountAmount = order.DiscountAmount;
            handlingFee = order.HandlingFee;
            shippingPrice = order.ShippingPrice;
            taxAmount = order.TaxAmount;
            total = order.Total;

            if(discount.UsageNumber == 0)
                return 0;
            if(!String.IsNullOrWhiteSpace(discount.CouponCode))
            {
                string attrKey = Par.In("Rules").Of<FixedDiscountRuleEntity>().Name("CouponCode");
                string attrVal = order.Attribute(attrKey);
                if(!attrVal.Contains(discount.CouponCode))
                    return 0;
            }

            decimal value = discount.Flags.HasFlag(FixedDiscountRuleFlags.Percent)
                ? order.Total / 100 * discount.Amount
                : discount.Amount;
            discountAmount += value;
            total -= value;

            return 1;
        }

        public int Apply(IUnitOfWork uow, 
            SalesRuleEntity rule, 
            OrderItemEntity item,
            out decimal discountAmount,
            out decimal taxAmount,
            out decimal total)
        {
            var discount = rule as FixedDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");

            discountAmount = item.DiscountAmount;
            taxAmount = item.TaxAmount;
            total = item.Total;

            if(discount.UsageNumber == 0)
                return 0;

            decimal value = discount.Flags.HasFlag(FixedDiscountRuleFlags.Percent)
                ? item.Total / 100 * discount.Amount
                : discount.Amount;
            discountAmount += value;
            total -= value;

            return 1;
        }

        public int Commit(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
            var discount = history.Rule as FixedDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");
            if(discount.UsageNumber == 0)
                return -1;
            if(discount.UsageNumber < 0)
                return 1;
            uow.Execute(new UpdateFixedDiscountRule
            {
                EntityID = discount.EntityID,
                CommonFlags = discount.CommonFlags,
                ValidFrom = discount.ValidFrom,
                ValidTo = discount.ValidTo,
                UsageNumber = discount.UsageNumber - 1,
                Amount = discount.Amount,
                Name = discount.Name,
                Flags = discount.Flags
            });
            return 1;
        }

        public void Rollback(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
            var discount = history.Rule as FixedDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");
            if(discount.UsageNumber < 0)
                return;
            uow.Execute(new UpdateFixedDiscountRule
            {
                EntityID = discount.EntityID,
                CommonFlags = discount.CommonFlags,
                ValidFrom = discount.ValidFrom,
                ValidTo = discount.ValidTo,
                UsageNumber = discount.UsageNumber + 1,
                Amount = discount.Amount,
                Name = discount.Name,
                Flags = discount.Flags
            });
        }
    }

    /// <summary>
    /// Represents the volume discount rule handler.
    /// </summary>
    [Dependency("c5533617-972d-4fe9-9bd1-621e807b3288")]
    public sealed class VolumeDiscountRuleHandler : ISalesRuleHandler
    {
        public int Apply(IUnitOfWork uow,
            SalesRuleEntity rule,
            OrderEntity order,
            out decimal discountAmount,
            out decimal handlingFee,
            out decimal shippingPrice,
            out decimal taxAmount,
            out decimal total)
        {
            var discount = rule as VolumeDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");

            discountAmount = order.DiscountAmount;
            handlingFee = order.HandlingFee;
            shippingPrice = order.ShippingPrice;
            taxAmount = order.TaxAmount;
            total = order.Total;

            if(discount.Flags.HasFlag(VolumeDiscountRuleFlags.Total))
            {
                if(order.Total < discount.ThresholdLower)
                    return 0;
                if(discount.ThresholdUpper != Decimal.Zero &&
                    order.Total > discount.ThresholdUpper)
                {
                    return 0;
                }
            }
            else
            {
                if(order.Items.Count < discount.ThresholdLower)
                    return 0;
                if(discount.ThresholdUpper != Decimal.Zero &&
                    order.Items.Count > discount.ThresholdUpper)
                {
                    return 0;
                }
            }

            decimal value = discount.Flags.HasFlag(VolumeDiscountRuleFlags.Percent)
                ? order.Total / 100 * discount.Amount
                : discount.Amount;
            discountAmount += value;
            total -= value;

            return 1;
        }

        public int Apply(IUnitOfWork uow,
            SalesRuleEntity rule,
            OrderItemEntity item,
            out decimal discountAmount,
            out decimal taxAmount,
            out decimal total)
        {
            var discount = rule as VolumeDiscountRuleEntity;
            if(discount == null)
                throw new ArgumentException("rule");

            discountAmount = item.DiscountAmount;
            taxAmount = item.TaxAmount;
            total = item.Total;

            if(discount.Flags.HasFlag(VolumeDiscountRuleFlags.Total))
            {
                if(item.Total < discount.ThresholdLower)
                    return 0;
                if(discount.ThresholdUpper != Decimal.Zero &&
                    item.Total > discount.ThresholdUpper)
                {
                    return 0;
                }
            }
            else
            {
                if(item.Quantity < discount.ThresholdLower)
                    return 0;
                if(discount.ThresholdUpper != Decimal.Zero &&
                    item.Quantity > discount.ThresholdUpper)
                {
                    return 0;
                }
            }

            decimal value = discount.Flags.HasFlag(VolumeDiscountRuleFlags.Percent)
                ? item.Total / 100 * discount.Amount
                : discount.Amount;
            discountAmount += value;
            total -= value;

            return 1;
        }

        public int Commit(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
            return 1;
        }

        public void Rollback(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
        }
    }

    /// <summary>
    /// Represents the fixed tax rule handler.
    /// </summary>
    [Dependency("40f78bea-4e16-4bb9-9080-2d14890a337b")]
    public sealed class FixedTaxRuleHandler : ISalesRuleHandler
    {
        public int Apply(IUnitOfWork uow,
            SalesRuleEntity rule,
            OrderEntity order,
            out decimal discountAmount,
            out decimal handlingFee,
            out decimal shippingPrice,
            out decimal taxAmount,
            out decimal total)
        {
            var tax = rule as FixedTaxRuleEntity;
            if(tax == null)
                throw new ArgumentException("rule");

            discountAmount = order.DiscountAmount;
            handlingFee = order.HandlingFee;
            shippingPrice = order.ShippingPrice;
            taxAmount = order.TaxAmount;
            total = order.Total;
            decimal value = tax.Flags.HasFlag(FixedTaxRuleFlags.Percent)
                ? order.Subtotal / 100 * tax.Amount
                : tax.Amount;
            taxAmount += value;
            total += value;

            return 1;
        }

        public int Apply(IUnitOfWork uow,
            SalesRuleEntity rule,
            OrderItemEntity item,
            out decimal discountAmount,
            out decimal taxAmount,
            out decimal total)
        {
            var tax = rule as FixedTaxRuleEntity;
            if(tax == null)
                throw new ArgumentException("rule");

            discountAmount = item.DiscountAmount;
            taxAmount = item.TaxAmount;
            total = item.Total;
            decimal value = tax.Flags.HasFlag(FixedTaxRuleFlags.Percent)
                ? item.Subtotal / 100 * tax.Amount
                : tax.Amount;
            taxAmount += value;
            total += value;

            return 1;
        }

        public int Commit(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
            return 1;
        }

        public void Rollback(IUnitOfWork uow, SalesRuleHistoryEntity history)
        {
        }
    }
}
